from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
from flask_cors import CORS
import pandas as pd
from datetime import datetime
import subprocess
from sqlalchemy.exc import SQLAlchemyError

from DDI.test import run_ddi_test

from utils import update_task_status, get_task_info_by_id, get_compounds_by_libraries, get_similar_compounds
import os
from http.cookies import SimpleCookie
from typing import Tuple

os.environ['MKL_THREADING_LAYER'] = 'GNU'
os.environ['MKL_SERVICE_FORCE_INTEL'] = '1'

from utils import compute_tomito_similarity

# Initialize Flask app and SQLAlchemy
# Set up the Flask application and configure the database connection
# Using SQLite as the database

task_app = Flask(__name__)
CORS(task_app)  # Enable CORS for all routes

import os

db_path = os.path.abspath('./tasks.db')
print('数据库文件路径:', db_path)
task_app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{db_path}'
task_app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

# Initialize the SQLAlchemy instance for the Flask app
db = SQLAlchemy(task_app)

# Models
# Define Task, Library, and Model tables for the database
class Task(db.Model):
    # Define the Task table with fields such as task_id, task_name, etc.
    task_id = db.Column(db.Integer, primary_key=True)  # Primary key for Task table
    task_name = db.Column(db.String(255), nullable=False)  # Name of the task
    task_description = db.Column(db.Text, nullable=True)  # Description of the task
    target_name = db.Column(db.String(255), nullable=False)  # Target name related to the task
    target_sequence = db.Column(db.String(255), nullable=False)  # Target sequence information
    model_id = db.Column(db.Integer, nullable=False)  # Reference to the model used
    library_id = db.Column(db.Integer, nullable=False)  # Reference to the library used
    user_id = db.Column(db.Integer, nullable=False)  # ID of the user who created the task
    task_status = db.Column(db.String(50), default='in-progress')  # Status of the task (default is 'in-progress')
    create_time = db.Column(db.DateTime, default=datetime.utcnow)  # Creation time of the task
    complete_time = db.Column(db.DateTime, nullable=True)  # Completion time of the task (optional)


class Compound(db.Model):
    compound_id = db.Column(db.String(255), primary_key=True)
    compound_smiles = db.Column(db.String(255), nullable=False)
    library_id = db.Column(db.Integer, db.ForeignKey('library.library_id'), nullable=False)

    # ForeignKey relationship: each compound belongs to a specific library
    library = db.relationship('Library', backref=db.backref('compounds', lazy=True))


class TaskResult(db.Model):
    result_id = db.Column(db.Integer, primary_key=True)
    task_id = db.Column(db.Integer, db.ForeignKey('task.task_id'), nullable=False)
    compound_id = db.Column(db.String(255), db.ForeignKey('compound.compound_id'), nullable=False)
    formula = db.Column(db.String(255), nullable=False)
    weight = db.Column(db.Integer, nullable=False)
    logp = db.Column(db.Integer, nullable=False)
    psa = db.Column(db.Integer, nullable=False)
    rotable_bond = db.Column(db.Integer, nullable=False)
    affinity_score = db.Column(db.Float, nullable=False)

    # ForeignKey relationships
    task = db.relationship('Task', backref=db.backref('results', lazy=True))
    compound = db.relationship('Compound', backref=db.backref('results', lazy=True))


class Library(db.Model):
    # Define the Library table with fields such as library_id, library_name, etc.
    library_id = db.Column(db.Integer, primary_key=True)  # Primary key for Library table
    library_name = db.Column(db.String(255), nullable=False)  # Name of the library
    library_description = db.Column(db.Text, nullable=True)  # Description of the library
    user_id = db.Column(db.String(255), nullable=True)  # user_id, default null


class Model(db.Model):
    # Define the Model table with fields such as model_id, model_name, etc.
    model_id = db.Column(db.Integer, primary_key=True)  # Primary key for Model table
    model_name = db.Column(db.String(255), nullable=False)  # Name of the model
    model_description = db.Column(db.Text, nullable=True)  # Description of the model


# Routes
# Define the Flask routes for different API endpoints

# Get all tasks for a specific user
@task_app.route('/tasks/<int:user_id>', methods=['GET'])
def get_tasks_by_user(user_id):
    tasks = Task.query.filter_by(user_id=user_id).all()  # Query tasks by user ID
    return jsonify([task.to_dict() for task in tasks])  # Return list of tasks as JSON


# Search tasks by various parameters
@task_app.route('/tasks/search', methods=['GET'])
def search_tasks():
    # Get query parameters from the request
    user_id = request.args.get('user_id')
    task_name = request.args.get('task_name')
    task_status = request.args.get('task_status')
    start_time = request.args.get('start_time')
    end_time = request.args.get('end_time')

    # Base query for tasks filtered by user ID
    query = Task.query.filter_by(user_id=user_id)

    # If task_name is provided, filter by name using case-insensitive like search
    if task_name:
        query = query.filter(Task.task_name.ilike(f'%{task_name}%'))
    # If task_status is provided, filter by status
    if task_status:
        query = query.filter_by(task_status=task_status)
    # If start_time and end_time are provided, filter by date range
    if start_time and end_time:
        try:
            # Convert start_time and end_time strings to datetime objects
            start_time = datetime.strptime(start_time, '%Y-%m-%d %H:%M:%S')
            end_time = datetime.strptime(end_time, '%Y-%m-%d %H:%M:%S')
            query = query.filter(Task.create_time >= start_time, Task.create_time <= end_time)
        except ValueError:
            # Return error if date format is invalid
            return jsonify({'error': 'Invalid date format. Use YYYY-MM-DD HH:MM:SS'}), 400

    # Execute the query and return the list of tasks
    tasks = query.all()
    return jsonify([task.to_dict() for task in tasks])


# Create a new task
@task_app.route('/tasks', methods=['POST'])
def create_task():
    # Get task data from the request
    data = request.get_json()

    # Validate required fields
    required_fields = ['task_name', 'target_name', 'target_sequence', 'model_id', 'library_id', 'user_id']
    for field in required_fields:
        if field not in data:
            return jsonify({'error': f'Missing required field: {field}'}), 400

    # Create a new Task instance with the provided data
    new_task = Task(
        task_name=data['task_name'],
        task_description=data.get('task_description'),
        target_name=data['target_name'],
        target_sequence=data['target_sequence'],
        model_id=data['model_id'],
        library_id=data['library_id'],
        user_id=data['user_id'],
        create_time=datetime.now()  # 不格式化，直接传递 datetime 对象
    )

    # Add the new task to the database and commit the transaction
    try:
        db.session.add(new_task)
        db.session.commit()
    except Exception as e:
        db.session.rollback()
        return jsonify({'error': f'Failed to create task: {str(e)}'}), 500

    # 从Task表中查询刚插入的任务ID
    task_id = new_task.task_id

    # 确保日志目录存在
    log_dir = 'log'
    if not os.path.exists(log_dir):
        os.makedirs(log_dir)

    # 在控制台的当前目录下执行命令
    command = f"nohup python -u screening.py {task_id} > log/{task_id}.txt 2>&1 &"
    print('command', command)
    try:
        subprocess.Popen(command, shell=True)
    except Exception as e:
        update_task_status(task_id, 'error')
        return jsonify({'error': f'Failed to execute command: {str(e)}'}), 500

    # 如果上述的都执行没问题，那么：Return the created task as JSON with status code 201
    return jsonify(new_task.to_dict()), 201


# 上传一个包含化合物的新库
@task_app.route('/libraries/upload', methods=['POST'])
def upload_library():
    # 从请求表单中获取 user_id 和 library_name
    # user_id = request.form.get('user_id')

    user_id = 'drs101'

    library_name = request.form.get('library_name')
    library_description = request.form.get('library_description', '')

    # 验证必填字段
    if not user_id or not library_name:
        return jsonify({'error': '用户ID和库名称为必填项'}), 400

    # 检查库名称是否已经存在
    existing_library = Library.query.filter_by(library_name=library_name).first()
    if existing_library:
        return jsonify({'error': '库名称已存在，请使用其他名称'}), 400

    # 获取上传的文件
    file = request.files.get('compound_file')
    if not file or file.filename == '':
        return jsonify({'error': '必须上传化合物文件'}), 400

    # 读取文件并提取 SMILES 字符串
    try:
        smiles_list = file.read().decode('utf-8').splitlines()
    except UnicodeDecodeError:
        return jsonify({'error': '化合物文件解码失败，请上传正确的文件格式'}), 400

    if not smiles_list:
        return jsonify({'error': '化合物文件为空或无效'}), 400

    # 将新库插入到 Library 表中
    new_library = Library(library_name=library_name, library_description=library_description, user_id=user_id)
    db.session.add(new_library)
    db.session.commit()

    # 将化合物插入到 Compound 表中
    compounds = []
    for index, smile in enumerate(smiles_list):
        compound = Compound(compound_id=f"{library_name}_{index}", compound_smiles=smile,
                            library_id=new_library.library_id)
        compounds.append(compound)

    db.session.bulk_save_objects(compounds)
    db.session.commit()

    return jsonify(
        {'message': '库和化合物上传成功', 'library_id': new_library.library_id}), 201


# Delete a task by task_id
@task_app.route('/tasks/<int:task_id>', methods=['DELETE'])
def delete_task(task_id):
    try:
        # Get the task by ID, or return 404 if not found
        task = Task.query.get_or_404(task_id)
        # Delete all task_result records with the same task_id
        TaskResult.query.filter_by(task_id=task_id).delete()
        # Delete the task from the database
        db.session.delete(task)
        # Commit the transaction
        db.session.commit()
        # Return success message
        return jsonify({'message': 'Task and related task results deleted successfully'}), 200
    except SQLAlchemyError as e:
        # Rollback the transaction in case of error
        db.session.rollback()
        # Return error message and log the exception
        return jsonify({'error': 'An error occurred while trying to delete the task or related task results',
                        'details': str(e)}), 500


# Function to get the SMILES of a compound by its compound_id
def get_smile_by_id(compound_id):
    compound = Compound.query.filter_by(compound_id=compound_id).first()
    if compound:
        return compound.compound_smiles
    return None  # Return None if no compound is found


import time


# Get task details by task_id
# @task_app.route('/tasks/details/<int:task_id>', methods=['GET'])
# def get_task_details(task_id):
#     # Get the task by ID, or return 404 if not found
#     task = Task.query.get_or_404(task_id)
#     # Get the associated library and model by their IDs
#     library = Library.query.get(task.library_id)
#     model = Model.query.get(task.model_id)
#
#     # Create a dictionary with detailed information about the task
#     task_details = {
#         'task_id': task.task_id,
#         'task_name': task.task_name,
#         'task_description': task.task_description,
#         'create_time': task.create_time,
#         'complete_time': task.complete_time,
#         'task_status': task.task_status,
#         'library_name': library.library_name if library else None,  # Include library name if available
#         'library_description': library.library_description if library else None,
#         'target_name': task.target_name,
#         'target_description': task.task_description,  # Description related to target
#         'model_name': model.model_name if model else None,  # Include model name if available
#         'model_description': model.model_description if model else None  # Include model name if available
#     }
#
#     # If the task is completed, fetch the associated results
#     if task.task_status == 'completed':
#         # 获取指定任务的筛选结果
#         results = TaskResult.query.filter_by(task_id=task.task_id).all()
#         # 获取化合物的SMILES列表
#         smiles_list = [result.compound.compound_smiles for result in results]
#         # 计算相似度矩阵的上三角部分
#         similarity_matrix = compute_tomito_similarity(smiles_list)
#         # 构建返回的结果，包含原有数据和相似度矩阵
#         task_details['results'] = [{
#             'result_id': result.result_id,
#             'compound_id': result.compound_id,
#             'smile': get_smile_by_id(result.compound_id),
#             'formula': result.formula,
#             'weight': result.weight,
#             'logp': result.logp,
#             'psa': result.psa,
#             'rotable_bond': result.rotable_bond,
#             'affinity_score': result.affinity_score
#         } for result in results]
#         task_details['tomito'] = similarity_matrix.tolist()
#
#     # Return the task details as JSON
#     return jsonify(task_details)

@task_app.route('/tasks/details/<int:task_id>', methods=['GET'])
def get_task_details(task_id):
    # 记录整个请求处理的开始时间
    request_start_time = datetime.now()

    # Step 1: Get the task by ID
    start_time = time.time()
    task = Task.query.get_or_404(task_id)
    end_time = time.time()
    print(f"Step 1: Get task by ID took {end_time - start_time:.4f} seconds")

    # Step 2: Get the associated library and model by their IDs
    start_time = time.time()
    library = Library.query.get(task.library_id)
    model = Model.query.get(task.model_id)
    end_time = time.time()
    print(f"Step 2: Get library and model took {end_time - start_time:.4f} seconds")

    # Step 3: Create task details dictionary
    start_time = time.time()
    task_details = {
        'task_id': task.task_id,
        'task_name': task.task_name,
        'task_description': task.task_description,
        'create_time': task.create_time,
        'complete_time': task.complete_time,
        'task_status': task.task_status,
        'library_name': library.library_name if library else None,
        'library_description': library.library_description if library else None,
        'target_name': task.target_name,
        'target_description': task.task_description,
        'model_name': model.model_name if model else None,
        'model_description': model.model_description if model else None
    }
    end_time = time.time()
    print(f"Step 3: Create task details dictionary took {end_time - start_time:.4f} seconds")

    # Step 4: If the task is completed, fetch results and compute similarity matrix
    if task.task_status == 'completed':
        # Step 4.1: Get task results
        start_time = time.time()
        results = TaskResult.query.filter_by(task_id=task.task_id).all()
        end_time = time.time()
        print(f"Step 4.1: Get task results took {end_time - start_time:.4f} seconds")

        # Step 4.2: Get SMILES list
        start_time = time.time()
        smiles_list = [result.compound.compound_smiles for result in results]
        end_time = time.time()
        print(f"Step 4.2: Get SMILES list took {end_time - start_time:.4f} seconds")

        # Step 4.3: Compute similarity matrix
        start_time = time.time()
        # similarity_matrix = compute_tomito_similarity(smiles_list)
        end_time = time.time()
        print(f"Step 4.3: Compute similarity matrix took {end_time - start_time:.4f} seconds")

        # Step 4.4: Construct results details
        start_time = time.time()
        task_details['results'] = [{
            'result_id': result.result_id,
            'compound_id': result.compound_id,
            'smile': get_smile_by_id(result.compound_id),
            'formula': result.formula,
            'weight': result.weight,
            'logp': result.logp,
            'psa': result.psa,
            'rotable_bond': result.rotable_bond,
            'affinity_score': result.affinity_score
        } for result in results]
        # task_details['tomito'] = similarity_matrix.tolist()
        end_time = time.time()
        print(f"Step 4.4: Construct results details took {end_time - start_time:.4f} seconds")

    # Step 5: Return the task details as JSON
    start_time = time.time()
    response = jsonify(task_details)
    end_time = time.time()
    print(f"Step 5: Return JSON response took {end_time - start_time:.4f} seconds")

    # 打印整个请求的处理时间
    request_end_time = datetime.now()
    total_duration = (request_end_time - request_start_time).total_seconds()
    print(f"Total request processing time: {total_duration:.4f} seconds")

    return response


@task_app.route('/ddi', methods=['POST'])
def predict():
    data = request.get_json()

    if data is None:
        return jsonify({"error_code": 400, "error_message": "Request must be JSON formatted"}), 400

    smile1 = data.get('smile1')
    smile2 = data.get('smile2')

    if not smile1 or not smile2:
        return jsonify({
            "error_code": 400,
            "error_message": "Both 'smile1' and 'smile2' fields are required in the request"
        }), 400

    if len(smile1) == 0 or len(smile2) == 0:
        return jsonify({
            "error_code": 400,
            "error_message": "SMILES strings 'smile1' and 'smile2' cannot be empty"
        }), 400

    smile1_list = [smile1] * 86
    smile2_list = [smile2] * 86

    result_json = run_ddi_test(smile1_list, smile2_list)

    if isinstance(result_json, dict) and "error_code" in result_json:
        return jsonify(result_json), 500  # 内部模型测试错误

    # 转换 result_json
    formatted_result = {}
    # 读取 interaction.csv 文件
    interaction_df = pd.read_csv('DDI/Interaction_information.csv')
    interaction_mapping = dict(zip(interaction_df['Interaction type'], interaction_df['Description']))

    for item in result_json:
        smile1_value = item.get('smile1')
        smile2_value = item.get('smile2')
        for type_id, prob in item.items():
            if type_id not in ['smile1', 'smile2']:
                description = interaction_mapping.get(int(type_id), '')
                if type_id not in formatted_result:
                    formatted_result[type_id] = []
                formatted_result[type_id].append({
                    'smile1': smile1_value,
                    'smile2': smile2_value,
                    'predict': prob,
                    'description': description
                })

    return jsonify(formatted_result)


# Directory to store PDB files
from flask import Response
from utils import predict_structure_and_save

@task_app.route('/predict', methods=['POST'])
def predict_structure():
    PDB_DIR = '/home/back-end/pdb'
    if not os.path.exists(PDB_DIR):
        os.makedirs(PDB_DIR)
    try:
        # Get the JSON data from request
        data = request.get_json()

        task_id = data.get('task_id')
        protein_name = data.get('target_name')
        if not task_id or not protein_name:
            return jsonify({'error': 'Missing task_id or protein_name'}), 400

        # Get the task information and extract the sequence
        _, _, _, sequence = get_task_info_by_id(task_id)

        # Check if the sequence length exceeds 400
        if len(sequence) > 400:
            return jsonify({'error': 'Sequence length exceeds max length of 400'}), 413  # Using 413 Payload Too Large

        pdb_file_path = os.path.join(PDB_DIR, f"{protein_name}.pdb")
        if os.path.exists(pdb_file_path):
            # If the PDB file exists, read and return its content as plain text
            with open(pdb_file_path, 'r') as pdb_file:
                pdb_content = pdb_file.read()
            return Response(pdb_content, mimetype='text/plain')
        else:
            # Predict and save the structure if the PDB file does not exist
            predict_structure_and_save(sequence, pdb_file_path)
            with open(pdb_file_path, 'r') as pdb_file:   # Read and return the generated PDB file content
                pdb_content = pdb_file.read()
            return Response(pdb_content, mimetype='text/plain')
    except Exception as e:
        return jsonify({'error': str(e)}), 500

import requests
from bs4 import BeautifulSoup
import json
@task_app.route('/get_admet', methods=['POST'])
def get_admet():
    # 获取请求中包含的SMILES参数
    smiles = request.json.get('SMILES', None)
    if not smiles:
        return jsonify({"error": "Missing SMILES parameter"}), 400

    # 请求URL
    url = "http://www.swissadme.ch/index.php"

    # 请求头
    headers = {
        "Content-Type": "application/x-www-form-urlencoded",
    }

    # 表单数据
    data = {
        "smiles": smiles  # 传递SMILES参数
    }

    try:
        # 发起POST请求
        response = requests.post(url, headers=headers, data=data)

        # 检查请求是否成功
        if response.status_code != 200:
            return jsonify({"error": f"Failed to retrieve data, status code: {response.status_code}"}), 500

        # 使用BeautifulSoup解析返回的HTML内容
        soup = BeautifulSoup(response.text, 'lxml')  # 使用lxml提高HTML解析速度和鲁棒性

        # 创建一个字典来存储ADMET属性
        admet_data = {}

        # 定义ADMET类别标题列表，用于识别不同类别
        category_titles = [
            "Physicochemical Properties",
            "Lipophilicity",
            "Water Solubility",
            "Pharmacokinetics",
            "Druglikeness",
            "Medicinal Chemistry"
        ]

        current_category = None

        # 遍历所有<tr>标签
        for tr in soup.find_all("tr"):
            # 获取所有<td>标签
            tds = tr.find_all("td")

            # 检查是否为类别标题
            if len(tds) == 1:
                td = tds[0]
                td_text = td.get_text(strip=True)

                # 如果文本在类别标题列表中，更新当前类别
                if td_text in category_titles and td.attrs.get('colspan') == '2' and td.attrs.get('align') == 'center':
                    current_category = td_text
                    if current_category not in admet_data:
                        admet_data[current_category] = {}

            # 如果存在两个<td>，并且当前类别已经确定
            elif len(tds) == 2 and current_category is not None:
                key_td = tds[0]
                value = tds[1].get_text(strip=True)

                # 使用递归提取key的核心名称，保留HTML结构中的<i>、<sub>等子标签内容
                def extract_key_text(element):
                    return "".join(
                        content.strip() if isinstance(content, str) else extract_key_text(content)
                        for content in element.contents if isinstance(content, str) or content.name not in ["a", "span"]
                    )

                key = extract_key_text(key_td)

                # 提取注释信息（如果存在）
                annotation = ""
                link = key_td.find("a", class_="help")
                if link and link.find("span"):
                    annotation = " ".join(
                        set(tag.get_text(strip=True) for tag in link.find("span").descendants if isinstance(tag, str))
                    )

                # 检查键名是否重复，如果重复则附加类别名以保持唯一性
                if key in admet_data[current_category]:
                    key = f"{key} ({current_category})"

                # 如果遇到属于Medicinal Chemistry的次级标题，强制将其划归到Medicinal Chemistry中
                if current_category != "Medicinal Chemistry" and key in ["PAINS", "Brenk", "Leadlikeness",
                                                                         "Synthetic accessibility"]:
                    if "Medicinal Chemistry" not in admet_data:
                        admet_data["Medicinal Chemistry"] = {}
                    admet_data["Medicinal Chemistry"][key] = {
                        "value": value,
                        "annotation": annotation
                    }
                else:
                    # 否则正常存储到当前类别中
                    admet_data[current_category][key] = {
                        "value": value,
                        "annotation": annotation
                    }

        # 返回提取的数据作为JSON响应
        return jsonify(admet_data), 200

    except Exception as e:
        return jsonify({"error": str(e)}), 500

@task_app.route('/compounds/similar', methods=['POST'])
def find_similar_compounds():
    # 从请求中获取目标化合物的 SMILES
    data = request.get_json()
    target_smiles = data.get('target_smiles')
    if not target_smiles:
        return jsonify({'error': 'Target SMILES is required'}), 400

    # 获取library_id为8和10的化合物
    libraries_to_search = [8, 10]
    compounds = get_compounds_by_libraries(libraries_to_search)
    # 计算相似化合物
    similar_compounds, error = get_similar_compounds(target_smiles, compounds)
    if error:
        return jsonify({'error': error}), 400

    return jsonify(similar_compounds)

from flask import Flask, send_from_directory
# 假设site文件夹在Flask项目根目录
@task_app.route('/docs/<path:filename>')
def serve_docs(filename):
    doc_path = os.path.join(task_app.root_path, 'site')

    # 如果filename是目录，默认查找index.html
    if os.path.isdir(os.path.join(doc_path, filename)):
        return send_from_directory(doc_path, os.path.join(filename, 'index.html'))

    return send_from_directory(doc_path, filename)

# 添加一个重定向到首页的快捷方式
@task_app.route('/docs/')
def docs_index():
    return send_from_directory(os.path.join(task_app.root_path, 'site'), 'index.html')

# Get all libraries for a specific user or shared with all users
@task_app.route('/libraries', methods=['GET'])
def get_libraries():
    # Get user_id from request arguments
    # user_id = request.args.get('user_id')

    user_id = 'drs101'

    # Validate that user_id is provided
    if not user_id:
        return jsonify({'error': 'user_id is required'}), 400

    # Query libraries that are either shared (user_id == null) or owned by the specified user
    libraries = Library.query.filter((Library.user_id == None) | (Library.user_id == user_id)).all()

    # Return a list of libraries as JSON
    return jsonify([{
        'library_id': library.library_id,
        'library_name': library.library_name,
        'library_description': library.library_description
    } for library in libraries])


# Get all models
@task_app.route('/models', methods=['GET'])
def get_models():
    # Query all models from the database
    models = Model.query.all()
    # Return a list of models as JSON
    return jsonify([{
        'model_id': model.model_id,
        'model_name': model.model_name,
        'model_description': model.model_description
    } for model in models])


# Utility function to serialize Task object
# Convert Task instance to dictionary format for JSON response
def to_dict(self):
    return {
        'task_id': self.task_id,
        'task_name': self.task_name,
        'task_description': self.task_description,
        'target_name': self.target_name,
        'target_sequence': self.target_sequence,
        'model_id': self.model_id,
        'library_id': self.library_id,
        'user_id': self.user_id,
        'task_status': self.task_status,
        'create_time': self.create_time,
        'complete_time': self.complete_time
    }


# Attach the to_dict method to the Task model
Task.to_dict = to_dict

# Main entry point
if __name__ == "__main__":
    task_app.run(host="0.0.0.0", port=50004)
